﻿
namespace Patterns4Net.ArchitectureExplorer.Documentation
{
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Diagnostics.Contracts;
    using System.IO;
    using System.Xml.Linq;
    using System.Xml.XPath;
    using Mono.Cecil;
    using Patterns4Net.Core.CecilExtensions;

    public enum ApiDocSearchResult
    {
        /// <summary>
        /// The API documentation was found.
        /// </summary>
        Found,

        /// <summary>
        /// The API documentation was not found because 
        /// the xml file with documentation for assembly 
        /// that contain the memeber was not found.
        /// </summary>
        AssemblyApiDoesNotExist,

        /// <summary>
        /// The API documentation was not found, but 
        /// xml file with documentation for assembly 
        /// that contain the memeber was found.
        /// </summary>
        NotFound,

        /// <summary>
        /// The API documentation of given member is not supported.
        /// </summary>
        NotSupported
    }

    [Export(typeof(ApiDocProvider))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class ApiDocProvider
    {
        private IDictionary<AssemblyDefinition, XDocument> docFiles = new Dictionary<AssemblyDefinition, XDocument>();

        public void AddAssembly(AssemblyDefinition assembly, string filename)
        {
            var directory = Path.GetDirectoryName(filename);
            var file = Path.GetFileNameWithoutExtension(filename);
            try
            {
                this.docFiles.Add(assembly, XDocument.Load(Path.Combine(directory, file + ".xml")));
            }
            catch
            {
                this.docFiles.Add(assembly, null);
            }
        }

        public ApiDocSearchResult GetDocumentation(MemberReference member, out XElement documentation)
        {
            documentation = null;
            XDocument document;
            if (this.docFiles.TryGetValue(member.Module.Assembly, out document) == false)
            {
                Contract.Assert(false, string.Format("The assembly of the member {0} was not added to ApiDocProvider.", member));
                return ApiDocSearchResult.NotSupported;
            }

            if (document == null)
            {
                return ApiDocSearchResult.AssemblyApiDoesNotExist;
            }

            var xpath = string.Format("//member[@name='{0}']", (object)this.GetAttributeValue((dynamic)member));
            if (string.IsNullOrEmpty(xpath))
            {
                return ApiDocSearchResult.NotSupported;
            }

            documentation = document.XPathSelectElement(xpath);
            return documentation != null ? ApiDocSearchResult.Found : ApiDocSearchResult.NotFound;
        }

        private string GetAttributeValue(MemberReference memberReference)
        {
            return string.Empty;
        }

        private string GetAttributeValue(TypeReference typeReference)
        {
            return "T:" + typeReference.GetReflectionFullName();
        }
    }
}
